package p.ithorns.sample.module;

import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import p.ithorns.sample.ModulePath;

import javax.annotation.PostConstruct;
import javax.servlet.ServletContext;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ModulePathHandlerMapping
 *
 * @author Ht.L
 * @date 2024-03-08 15:51
 * @see org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.MappingRegistry#register
 * @since 1.0.0
 */
public class ModulePathHandlerMapping extends RequestMappingHandlerMapping {

    private final Map<String, ModulePathCondition> conditions = new ConcurrentHashMap<>(32);

    private String contextPath;

    @PostConstruct
    public synchronized void init() {
        WebApplicationContext context = getWebApplicationContext();
        ServletContext sc = Objects.requireNonNull(context).getServletContext();
        this.contextPath = Objects.requireNonNull(sc).getContextPath();
    }

    @Override
    protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
        if (handlerType.equals(BasicErrorController.class)) {
            return new ModulePathCondition(ModuleConst.ERROR_PATH);
        }

        if (isCandidate(handlerType)) {
            // ApplicationContext context = getApplicationContext();
            // Object bean = Objects.requireNonNull(context).getBean(handlerType);
            ModulePath modulePath = handlerType.getAnnotation(ModulePath.class);
            if (null != modulePath) {
                String path = modulePath.path();
                ModulePathCondition condition = conditions.get(path);
                if (null == condition) {
                    condition = buildFrom(modulePath);
                    conditions.put(path, condition);
                }
                return condition;
            }
        }
        return null;
    }


    private boolean isCandidate(Class<?> clazz) {
        int modifiers = clazz.getModifiers();
        return !(Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers))
                && clazz.isAnnotationPresent(ModulePath.class);
    }

    @Override
    protected RequestCondition<?> getCustomMethodCondition(Method method) {
        ModulePath annotation = AnnotationUtils.findAnnotation(method, ModulePath.class);
        if (null == annotation) {
            // 方法上没有，看类上是否有
            return getCustomTypeCondition(method.getDeclaringClass());
        }
        return buildFrom(annotation);
    }

    private ModulePathCondition buildFrom(ModulePath module) {
        return null == module
                ? new ModulePathCondition("/")
                : new ModulePathCondition(contextPath + module.path());
    }

}